package com.sunclouder.cactus.common.advice;


import cn.hutool.json.JSONException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.sunclouder.cactus.common.MessageSourceAware;
import com.sunclouder.cactus.common.api.response.ResponseBean;
import com.sunclouder.cactus.common.api.response.ResponseCode;
import com.sunclouder.cactus.common.api.response.ResponseException;
import com.sunclouder.cactus.util.LogUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;

import static com.sunclouder.cactus.common.api.response.ResponseCode.*;
import static com.sunclouder.cactus.common.api.response.ResponseHelper.failure;


/**
 * Description: controller 切面异常处理 date: 2023/9/23
 *
 * @author yan.zhao
 */
@Slf4j
@RestControllerAdvice(annotations = {com.sunclouder.cactus.common.annotation.ResponseResult.class})
public class ExceptionHandlerAdvice {
    /**
     * 统一处理参数校验错误异常,此处可以拦截实体类校验不通过产生的异常， 但是对于get接口的异常或者path上的参数异常获取不到
     *
     * @param e
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseBean<Object> processValidException(MethodArgumentNotValidException e) {
        FieldError fieldError = e.getBindingResult().getFieldError();
        String fieldErrorMessage = "NONE";
        if (fieldError != null) {
            fieldErrorMessage = fieldError.getField() + ":" + fieldError.getDefaultMessage();
        }
        log.error("param error:{}", LogUtils.getLogException(e));
        return paramErrorWrapper(fieldErrorMessage);
    }

    /**
     * 统一处理参数校验错误异常， 拦截get或者path上面的参数的异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler({ConstraintViolationException.class, UnsupportedOperationException.class})
    public ResponseBean<Object> processValidException(ConstraintViolationException e) {
        String fieldErrorMessage = "NONE";
        for (ConstraintViolation<?> constraintViolation : e.getConstraintViolations()) {
            log.error("param error:{}", constraintViolation.getMessage());
            fieldErrorMessage = constraintViolation.getMessage();
            break;
        }
        log.error("param error:{}", LogUtils.getLogException(e));
        return paramErrorWrapper(fieldErrorMessage);
    }

    /**
     * 接口层 json参数转换或参数转换异常异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler({InvalidFormatException.class, HttpMessageNotReadableException.class})
    public ResponseBean<Object> exceptionHandlerParse(Exception e) {
        log.info("parse error is {}", LogUtils.getLogException(e));
        return failureWrapper(PARSE_ERROR);
    }

    /**
     * 服务层 json参数转换异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler({JSONException.class, JsonProcessingException.class})
    public ResponseBean<Object> exceptionHandlerJson(Exception e) {
        log.info("parse error is {}", LogUtils.getLogException(e));
        return failureWrapper(PARSE_ERROR);
    }

    @ExceptionHandler(Exception.class)
    public ResponseBean<Object> exceptionHandler(Exception e) {
        log.error("system error is {}", LogUtils.getLogException(e));
        return failureWrapper(SYSTEM_ERROR);
    }

    @ExceptionHandler(ResponseException.class)
    public ResponseBean<Object> exceptionHandler(ResponseException e) {
        log.error("base exception is {}", LogUtils.getLogException(e));
        ResponseCode responseCode = e.getCode();
        String message = MessageSourceAware.getOrDefault(String.valueOf(responseCode.getCode()), responseCode.getMessage());
        if (e.getExtraArgs() != null && e.getExtraArgs().length > 0) {
            message = String.format(message, e.getExtraArgs());
        }
        return failure(responseCode.getCode(), message);
    }

    private ResponseBean<Object> paramErrorWrapper(String fieldErrorMessage) {
        // 先国际化，再替换模版
        String message = MessageSourceAware.getOrDefault(String.valueOf(PARAM_ERROR.getCode()), PARAM_ERROR.getMessage());
        message = String.format(message, fieldErrorMessage);
        return failure(PARAM_ERROR.getCode(), message);
    }

    private ResponseBean<Object> failureWrapper(ResponseCode e) {
        return failure(e.getCode(), getMessage(e));
    }

    private String getMessage(ResponseCode e) {
        return MessageSourceAware.getOrDefault(String.valueOf(e.getCode()), e.getMessage());
    }
}
